﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace AKVAsmart.AFS.VPA.Entities
{
	public interface IHashtableReadOnly<K, V> : IEnumerable<KeyValuePair<K, V>>
	{
		int Count { get; }
		V this[K key] { get; }
		IEqualityComparer<K> Comparer { get; }
		IEnumerable<K> Keys { get; }
		IEnumerable<V> Values { get; }
		bool ContainsKey(K key);
		bool ContainsValue(V val);
		bool TryGetValue(K key, out V val);
	}

	[Serializable]
	public class Hashtable<K, V> : Dictionary<K, V>, IHashtableReadOnly<K, V>
	{
		public Hashtable() { }

		public Hashtable(IDictionary<K, V> ht)
			: base(ht)
		{
		}

		protected Hashtable(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}

		public Hashtable(IEqualityComparer<K> comparer) : base(comparer)
		{
		}

		public Hashtable(int capacity) : base(capacity)
		{
		}

		public Hashtable(IDictionary<K, V> dictionary, IEqualityComparer<K> comparer) : base(dictionary, comparer)
		{
		}

		public Hashtable(int capacity, IEqualityComparer<K> comparer) : base(capacity, comparer)
		{
		}

		public Hashtable<K, V> Clone()
		{
			return new Hashtable<K, V>(this); // FIX! shallow copy is dangerous. changes in the "clone"'s reference type objects, will also change in the original
		}

		// example usage: ht[flag, () => new CostingFLAG(periodID, flag)];
		public V this[K key, Func<V> newItemIfMissing]
		{
			get
			{
				V val = this[key];
				if (val == null)
				{
					val = newItemIfMissing();
					this.Add(key, val);
				}
				return val;
			}
		}

		public new V this[K key]
		{
			get
			{
				V value;
				if (TryGetValue(key, out value))
					return value;
				else
					return value;
					//return default(V); // a default value is already assigned implicitly in TryGetValue
			}
			set
			{
				base[key] = value;
			}
		}

		//public V this[K key, V defaultValueIfMissing]
		//{
		//    get
		//    {
		//        V value;
		//        if (TryGetValue(key, out value))
		//            return value;
		//        else
		//            return defaultValueIfMissing;
		//    }
		//}

		public V this[K key, bool exceptionIfMissing]
		{
			get
			{
				if (exceptionIfMissing)
					return base[key]; //base will throw exception if key not exists
				else
					return this[key]; //we will return default value if not exists
			}
			set
			{
				base[key] = value;
			}
		}

		public K GetFirstKey()
		{
			return new List<K>(this.Keys)[0];
		}

		public V GetFirstValue()
		{
			return new List<V>(this.Values)[0];
		}

		public static Hashtable<K, List<V>> GroupByKey(IEnumerable<V> collection, Func<V, K> keyGen)
		{
			Hashtable<K, List<V>> htResult = new Hashtable<K,List<V>>();
			foreach (V val in collection)
			{
				K key = keyGen(val);
				List<V> valueColl = htResult[key];
				if (valueColl == null)
				{
					valueColl = new List<V>();
					htResult[key] = valueColl;
				}
				valueColl.Add(val);
			}
			return htResult;
		}

		#region IHashtableReadOnly<K,V> Members

		int IHashtableReadOnly<K, V>.Count
		{
			get { return this.Count; }
		}

		V IHashtableReadOnly<K, V>.this[K key]
		{
			get { return this[key]; }
		}

		IEqualityComparer<K> IHashtableReadOnly<K, V>.Comparer
		{
			get { return this.Comparer; }
		}

		IEnumerable<K> IHashtableReadOnly<K, V>.Keys
		{
			get { return this.Keys; }
		}

		IEnumerable<V> IHashtableReadOnly<K, V>.Values
		{
			get { return this.Values; }
		}

		bool IHashtableReadOnly<K, V>.ContainsKey(K key)
		{
			return this.ContainsKey(key);
		}

		bool IHashtableReadOnly<K, V>.ContainsValue(V val)
		{
			return this.ContainsValue(val);
		}

		bool IHashtableReadOnly<K, V>.TryGetValue(K key, out V val)
		{
			return this.TryGetValue(key, out val);
		}

		#endregion
	}

	public interface IHashListKey<K>
	{
		K HashListKey { get; }
	}

	//[Serializable]
	//public class HashList<K, V> : ICollection<V>
	//	where V : IHashListKey<K>
	//{
	//	private Hashtable<K, List<V>> pHashtable = new Hashtable<K, List<V>>();

	//	public List<V> this[K key]
	//	{
	//		get
	//		{
	//			List<V> elements = this.pHashtable[key];
	//			if (elements == null)
	//				return new List<V>();
	//			return elements;
	//		}
	//	}

	//	public void AddRange(IEnumerable<V> items)
	//	{
	//		foreach (V item in items)
	//			this.Add(item);
	//	}

	//	public bool RemoveRange(K key)
	//	{
	//		if (this.pHashtable.ContainsKey(key))
	//		{
	//			this.pHashtable.Remove(key);
	//			return true;
	//		}
	//		return false;
	//	}

	//	#region ICollection<V> Members

	//	public void Add(V item)
	//	{
	//		List<V> list = null;
	//		if (!pHashtable.TryGetValue(item.HashListKey, out list))
	//		{
	//			list = new List<V>();
	//			pHashtable.Add(item.HashListKey, list);
	//		}
	//		list.Add(item);
	//	}

	//	public void Clear()
	//	{
	//		pHashtable.Clear();
	//	}

	//	public bool Contains(V item)
	//	{
	//		List<V> list = null;
	//		if (!pHashtable.TryGetValue(item.HashListKey, out list))
	//			return false;
	//		return list.Contains(item);
	//	}

	//	public void CopyTo(V[] array, int arrayIndex)
	//	{
	//		List<V> list = new List<V>();
	//		foreach (V item in this)
	//			list.Add(item);
	//		list.CopyTo(array, arrayIndex);
	//	}

	//	public int Count
	//	{
	//		get 
	//		{ 
	//			int count = 0;
	//			foreach (List<V> list in pHashtable.Values)
	//				count += list.Count;
	//			return count;
	//		}
	//	}

	//	public bool IsReadOnly
	//	{
	//		get { return false; }
	//	}

	//	public bool Remove(V item)
	//	{
	//		List<V> list = null;
	//		if (!pHashtable.TryGetValue(item.HashListKey, out list))
	//			return false;
	//		bool result = list.Remove(item);
	//		if (list.Count == 0)
	//			pHashtable.Remove(item.HashListKey);
	//		return result;
	//	}

	//	#endregion

	//	#region IEnumerable<V> Members

	//	public IEnumerator<V> GetEnumerator()
	//	{
	//		foreach (List<V> list in pHashtable.Values)
	//			foreach (V item in list)
	//				yield return item;
	//	}

	//	#endregion

	//	#region IEnumerable Members

	//	System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
	//	{
	//		return this.GetEnumerator();
	//	}

	//	#endregion
	//}
}
